package 基于单飞模式的请求管理;

import jdk.nashorn.internal.codegen.CompilerConstants;

import java.util.HashMap;
import java.util.concurrent.*;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Supplier;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: 25228
 * Date: 2024-12-22
 * Time: 15:21
 */

public class SingleFlightDemo {
    public static void main(String[] args) throws InterruptedException {
        CallManage callManage = new CallManage();

        // 测试用例 1：单线程测试
        String key = "testKey";
        byte[] result = callManage.run(key, () -> "Hello, World!".getBytes());
        // ↓ 输出 "Hello, World!"
        System.out.println("Test 1 Result: " + new String(result));

        // 测试用例 2：多线程重复请求测试
        ExecutorService executor = Executors.newFixedThreadPool(5);
        String repeatKey = "repeatKey";
        Runnable task = () -> {
            byte[] res = callManage.run(repeatKey, () -> {
                try {
                    Thread.sleep(100); // 模拟任务耗时
                    System.out.println("I appeal only once: " + repeatKey);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                return "OnlyOnce".getBytes();
            });
            System.out.println("Test 2 Result: " + new String(res));
        };
        for (int i = 0; i < 5; i++) {
            executor.execute(task);
        }
        executor.shutdown();
        executor.awaitTermination(1, TimeUnit.SECONDS);

        // 测试用例 3：多个 key 并发测试
        ExecutorService multiKeyExecutor = Executors.newFixedThreadPool(3);
        for (int i = 1; i <= 3; i++) {
            String multiKey = "key" + i;
            multiKeyExecutor.execute(() -> {
                byte[] res = callManage.run(multiKey, () -> {
                    return ("ValueFor" + multiKey).getBytes();
                });
                System.out.println("Test 3 Result for " + multiKey + ": " + new String(res));
            });
        }
        multiKeyExecutor.shutdown();
        multiKeyExecutor.awaitTermination(1, TimeUnit.SECONDS);

        // 测试用例 4：错误输入测试
        try {
            callManage.run(null, () -> "InvalidKey".getBytes());
        } catch (Exception e) {
            System.out.println("Test 4: Caught exception for null key");
        }

        try {
            callManage.run("invalidFunc", null);
        } catch (Exception e) {
            System.out.println("Test 4: Caught exception for null func");
        }
    }
}

class Call{
    //存储任务的结果
    byte[] val;
    //用于同步线程，确保只有一个线程实际执行任务
    CountDownLatch latch;

    public byte[] getVal() {
        return val;
    }

    public void setVal(byte[] val) {
        this.val = val;
    }

    //初始化 CountDownLatch，设置计数器为 1
    void lock(){
        latch=new CountDownLatch(1);
    }

    //阻塞调用线程，等待 done() 方法被调用
    void await() throws InterruptedException {
        latch.await();
    }
    void done(){
        latch.countDown();
    }
}

class CallManage {
    private final ReentrantLock lock;
    private final ConcurrentHashMap<String, Call> callMap;

    CallManage() {
        lock = new ReentrantLock();
        callMap = new ConcurrentHashMap<>();
    }

    byte[] run(String key, Supplier<byte[]> func) {
        if (key == null || func == null) {
            System.out.println("key 和 func 不能为空");
            return null;
        }

        Call call;
        lock.lock();
        try {
            // 使用 putIfAbsent 避免多个线程同时创建 Call 对象
            call = callMap.putIfAbsent(key, new Call());
            if (call == null) {
                call = callMap.get(key);
                call.lock();
                try {
                    // 执行任务，设置结果，调用 done()
                    byte[] result = func.get();
                    call.setVal(result);
                    call.done();
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            } else {
                lock.unlock();
            }
        } catch (Exception e) {
            lock.unlock();
            throw e;
        }

        try {
            call.await();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            // 可以添加更多中断处理逻辑，例如抛出运行时异常或退出程序
            throw new RuntimeException("Thread interrupted while waiting for call", e);
        }
        return call.getVal();
    }
}